using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using LP;
using UnityEngine;

public enum EAudioSuffix
{
    mp3,
    wav
}

public class AudioMgr : AbsMgr, IDontDestroy
{
    private GameObject audioGo;
    private AudioSource bgSource;

    private AudioSource aiSource;

    /// <summary>
    /// 空闲的AudioSource
    /// </summary>
    private List<AudioSource> idlePool = new List<AudioSource>();

    /// <summary>
    /// 工作中的AudioSource
    /// </summary>
    private List<AudioSource> workPool = new List<AudioSource>();

    /// <summary>
    /// 音效源最大数量
    /// </summary>
    private const int EffectSouceMaxCount = 5;

    private List<Coroutine> audioEffectCoroutines = new List<Coroutine>();
    private List<Action> audioEffectCallback = new List<Action>();

    public override void OnInit()
    {
        base.OnInit();
        Global.SysEventHandler.RegHandler(ESys.OnSceneChange, OnSceneChange, TokenSource.Token);
        audioGo = new GameObject("AudioGameObject");
        audioGo.transform.position = Vector3.zero;
        bgSource = audioGo.AddComponent<AudioSource>();
        bgSource.volume = 0.7f;
        bgSource.playOnAwake = false;
        for (int i = 0; i < EffectSouceMaxCount; i++)
        {
            AudioSource effectSource = audioGo.AddComponent<AudioSource>();
            effectSource.playOnAwake = false;
            effectSource.volume = 1f;
            effectSource.loop = false;
            idlePool.Add(effectSource);
        }

        aiSource = audioGo.AddComponent<AudioSource>();
        aiSource.playOnAwake = false;
        aiSource.volume = 1f;
        DontDestroy();
    }

    public override void OnDestroy()
    {
        // effectClipsQueue.Clear();
        base.OnDestroy();
    }

    public void PlayBgAudio(string fullName, EAudioSuffix suffix = EAudioSuffix.mp3, bool isLoop = true)
    {
        Global.ResMgrIns.LoadAssetAsync<AudioClip>($"{fullName}.{Enum.GetName(typeof(EAudioSuffix), suffix)}", (re) =>
        {
            if (re != null)
            {
                bgSource.clip = re;
                bgSource.Play();
                bgSource.loop = isLoop;
            }
        });
    }

    public void PasueBgAudio()
    {
        bgSource.Pause();
    }

    public void ResumeBgAudio()
    {
        if (bgSource.clip != null)
        {
            bgSource.Play();
        }
    }

    public void StopBgAudio()
    {
        if (bgSource != null && bgSource.clip)
        {
            bgSource.Stop();
            bgSource.clip = null;
        }
    }

    public void PlayAIAudio(AudioClip clip, bool isLoop = false)
    {
        if (clip != null)
        {
            aiSource.clip = clip;
            aiSource.loop = isLoop;
            aiSource.Play();
        }
    }

    public void PasueAIAudio()
    {
        aiSource.Pause();
    }

    public void ResumeAIAudio()
    {
        if (aiSource.clip != null)
        {
            aiSource.Play();
        }
    }

    public void StopAIAudio()
    {
        if (aiSource != null && aiSource.clip != null)
        {
            aiSource.Stop();
            aiSource.clip = null;
        }
    }

    /// <summary>
    /// 播放音效
    /// </summary>
    /// <param name="fullName"></param>
    /// <param name="suffix">音频后缀</param>
    /// <param name="clipLengthCb">加载到AudioClip后回调返回该音效的长度</param>
    /// <param name="endCallback">播放结束时的回调</param>
    public void PlayEffectAudio(string fullName, EAudioSuffix suffix = EAudioSuffix.mp3,
        Action<float> clipLengthCb = null, Action endCallback = null)
    {
        Global.ResMgrIns.LoadAssetAsync<AudioClip>($"{fullName}.{Enum.GetName(typeof(EAudioSuffix), suffix)}", (re) =>
        {
            if (re != null)
            {
                if (idlePool.Count == 0)
                {
                    Debug.LogWarning("播放失败,EffectSource无空闲");
                }
                else
                {
                    clipLengthCb?.Invoke(re.length);
                    AudioSource audioSource = idlePool[0];
                    audioSource.clip = re;
                    audioSource.Play();
                    workPool.Add(audioSource);
                    idlePool.Remove(audioSource);
                    float musicLength = re.length;
                    Coroutine coroutine =
                        Global.CoroutineMgrIns.StartCoroutine(DelayCallback(endCallback, musicLength, audioSource),
                            TokenSource.Token);
                    audioEffectCoroutines.Add(coroutine);
                    audioEffectCallback.Add(endCallback);
                }
            }
        });
    }

    /// <summary>
    /// 停止所有音效
    /// </summary>
    public void StopAllEffect()
    {
        for (int i = 0; i < audioEffectCoroutines.Count; i++)
        {
            Global.CoroutineMgrIns.StopCoroutine(audioEffectCoroutines[i]);
        }

        audioEffectCoroutines.Clear();

        audioEffectCallback.Clear();

        for (int i = 0; i < workPool.Count; i++)
        {
            workPool[0].Stop();
            idlePool.Add(workPool[0]);
            workPool.RemoveAt(0);
        }
    }

    public void StopAllAudio()
    {
        if (bgSource != null)
        {
            bgSource.clip = null;
        }
        if (aiSource != null)
        {
            aiSource.clip = null;
        }
        var idleEnumerator = idlePool.GetEnumerator();
        while (idleEnumerator.MoveNext())
        {
            if (idleEnumerator.Current != null)
            {
                idleEnumerator.Current.clip = null;
            }
        }

        var workEnumerator = workPool.GetEnumerator();
        while (workEnumerator.MoveNext())
        {
            if (workEnumerator.Current != null)
            {
                workEnumerator.Current.clip = null;
            }
        }
    }

    public void DontDestroy()
    {
        GameObject.DontDestroyOnLoad(audioGo);
    }


    IEnumerator DelayCallback(Action callback, float delay, AudioSource audioSource)
    {
        yield return new WaitForSeconds(delay);
        if (audioSource != null)
        {
            workPool.Remove(audioSource);
            idlePool.Add(audioSource);
        }

        callback?.Invoke();
    }

    private void OnSceneChange(ITuple tuple)
    {
        StopAllAudio();
    }
}